home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tcl / dist / tclEnv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-12-05  |  10.9 KB  |  434 lines

  1. /* 
  2.  * tclEnv.c --
  3.  *
  4.  *    Tcl support for environment variables, including a setenv
  5.  *    procedure.
  6.  *
  7.  * Copyright 1991 Regents of the University of California
  8.  * Permission to use, copy, modify, and distribute this
  9.  * software and its documentation for any purpose and without
  10.  * fee is hereby granted, provided that this copyright
  11.  * notice appears in all copies.  The University of California
  12.  * makes no representations about the suitability of this
  13.  * software for any purpose.  It is provided "as is" without
  14.  * express or implied warranty.
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclEnv.c,v 1.7 91/09/23 11:22:21 ouster Exp $ SPRITE (Berkeley)";
  19. #endif /* not lint */
  20.  
  21. #include "tclInt.h"
  22. #include "tclUnix.h"
  23.  
  24. /*
  25.  * The structure below is used to keep track of all of the interpereters
  26.  * for which we're managing the "env" array.  It's needed so that they
  27.  * can all be updated whenever an environment variable is changed
  28.  * anywhere.
  29.  */
  30.  
  31. typedef struct EnvInterp {
  32.     Tcl_Interp *interp;        /* Interpreter for which we're managing
  33.                  * the env array. */
  34.     struct EnvInterp *nextPtr;    /* Next in list of all such interpreters,
  35.                  * or zero. */
  36. } EnvInterp;
  37.  
  38. static EnvInterp *firstInterpPtr;
  39.                 /* First in list of all managed interpreters,
  40.                  * or NULL if none. */
  41.  
  42. static int environSize = 0;    /* Non-zero means that the all of the
  43.                  * environ-related information is malloc-ed
  44.                  * and the environ array itself has this
  45.                  * many total entries allocated to it (not
  46.                  * all may be in use at once).  Zero means
  47.                  * that the environment array is in its
  48.                  * original static state. */
  49.  
  50. /*
  51.  * Declarations for local procedures defined in this file:
  52.  */
  53.  
  54. static void        EnvInit _ANSI_ARGS_((void));
  55. static char *        EnvTraceProc _ANSI_ARGS_((ClientData clientData,
  56.                 Tcl_Interp *interp, char *name1, char *name2,
  57.                 int flags));
  58. static int        FindVariable _ANSI_ARGS_((char *name, int *lengthPtr));
  59. void            setenv _ANSI_ARGS_((char *name, char *value));
  60. void            unsetenv _ANSI_ARGS_((char *name));
  61.  
  62. /*
  63.  *----------------------------------------------------------------------
  64.  *
  65.  * TclSetupEnv --
  66.  *
  67.  *    This procedure is invoked for an interpreter to make environment
  68.  *    variables accessible from that interpreter via the "env"
  69.  *    associative array.
  70.  *
  71.  * Results:
  72.  *    None.
  73.  *
  74.  * Side effects:
  75.  *    The interpreter is added to a list of interpreters managed
  76.  *    by us, so that its view of envariables can be kept consistent
  77.  *    with the view in other interpreters.  If this is the first
  78.  *    call to Tcl_SetupEnv, then additional initialization happens,
  79.  *    such as copying the environment to dynamically-allocated space
  80.  *    for ease of management.
  81.  *
  82.  *----------------------------------------------------------------------
  83.  */
  84.  
  85. void
  86. TclSetupEnv(interp)
  87.     Tcl_Interp *interp;        /* Interpreter whose "env" array is to be
  88.                  * managed. */
  89. {
  90.     EnvInterp *eiPtr;
  91.     int i;
  92.  
  93.     /*
  94.      * First, initialize our environment-related information, if
  95.      * necessary.
  96.      */
  97.  
  98.     if (environSize == 0) {
  99.     EnvInit();
  100.     }
  101.  
  102.     /*
  103.      * Next, add the interpreter to the list of those that we manage.
  104.      */
  105.  
  106.     eiPtr = (EnvInterp *) ckalloc(sizeof(EnvInterp));
  107.     eiPtr->interp = interp;
  108.     eiPtr->nextPtr = firstInterpPtr;
  109.     firstInterpPtr = eiPtr;
  110.  
  111.     /*
  112.      * Store the environment variable values into the interpreter's
  113.      * "env" array, and arrange for us to be notified on future
  114.      * writes and unsets to that array.
  115.      */
  116.  
  117.     (void) Tcl_UnsetVar2(interp, "env", (char *) NULL, TCL_GLOBAL_ONLY);
  118.     for (i = 0; ; i++) {
  119.     char *p, *p2;
  120.  
  121.     p = environ[i];
  122.     if (p == NULL) {
  123.         break;
  124.     }
  125.     for (p2 = p; *p2 != '='; p2++) {
  126.         /* Empty loop body. */
  127.     }
  128.     *p2 = 0;
  129.     (void) Tcl_SetVar2(interp, "env", p, p2+1, TCL_GLOBAL_ONLY);
  130.     *p2 = '=';
  131.     }
  132.     Tcl_TraceVar2(interp, "env", (char *) NULL,
  133.         TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
  134.         EnvTraceProc, (ClientData) NULL);
  135. }
  136.  
  137. /*
  138.  *----------------------------------------------------------------------
  139.  *
  140.  * FindVariable --
  141.  *
  142.  *    Locate the entry in environ for a given name.
  143.  *
  144.  * Results:
  145.  *    The return value is the index in environ of an entry with the
  146.  *    name "name", or -1 if there is no such entry.   The integer at
  147.  *    *lengthPtr is filled in with the length of name (if a matching
  148.  *    entry is found) or the length of the environ array (if no matching
  149.  *    entry is found).
  150.  *
  151.  * Side effects:
  152.  *    None.
  153.  *
  154.  *----------------------------------------------------------------------
  155.  */
  156.  
  157. static int
  158. FindVariable(name, lengthPtr)
  159.     char *name;            /* Name of desired environment variable. */
  160.     int *lengthPtr;        /* Used to return length of name (for
  161.                  * successful searches) or number of non-NULL
  162.                  * entries in environ (for unsuccessful
  163.                  * searches). */
  164. {
  165.     int i;
  166.     register char *p1, *p2;
  167.  
  168.     for (i = 0, p1 = environ[i]; p1 != NULL; i++, p1 = environ[i]) {
  169.     for (p2 = name; *p2 == *p1; p1++, p2++) {
  170.         /* NULL loop body. */
  171.     }
  172.     if ((*p1 == '=') && (*p2 == '\0')) {
  173.         *lengthPtr = p2-name;
  174.         return i;
  175.     }
  176.     }
  177.     *lengthPtr = i;
  178.     return -1;
  179. }
  180.  
  181. /*
  182.  *----------------------------------------------------------------------
  183.  *
  184.  * setenv --
  185.  *
  186.  *    Set an environment variable, replacing an existing value
  187.  *    or creating a new variable if there doesn't exist a variable
  188.  *    by the given name.
  189.  *
  190.  * Results:
  191.  *    None.
  192.  *
  193.  * Side effects:
  194.  *    The environ array gets updated, as do all of the interpreters
  195.  *    that we manage.
  196.  *
  197.  *----------------------------------------------------------------------
  198.  */
  199.  
  200. void
  201. setenv(name, value)
  202.     char *name;            /* Name of variable whose value is to be
  203.                  * set. */
  204.     char *value;        /* New value for variable. */
  205. {
  206.     int index, length, nameLength;
  207.     char *p;
  208.     EnvInterp *eiPtr;
  209.  
  210.     if (environSize == 0) {
  211.     EnvInit();
  212.     }
  213.  
  214.     /*
  215.      * Figure out where the entry is going to go.  If the name doesn't
  216.      * already exist, enlarge the array if necessary to make room.  If
  217.      * the name exists, free its old entry.
  218.      */
  219.  
  220.     index = FindVariable(name, &length);
  221.     if (index == -1) {
  222.     if ((length+2) > environSize) {
  223.         char **newEnviron;
  224.  
  225.         newEnviron = (char **) ckalloc((unsigned)
  226.             ((length+5) * sizeof(char *)));
  227.         memcpy((VOID *) newEnviron, (VOID *) environ,
  228.             length*sizeof(char *));
  229.         ckfree((char *) environ);
  230.         environ = newEnviron;
  231.         environSize = length+5;
  232.     }
  233.     index = length;
  234.     environ[index+1] = NULL;
  235.     nameLength = strlen(name);
  236.     } else {
  237.     ckfree(environ[index]);
  238.     nameLength = length;
  239.     }
  240.  
  241.     /*
  242.      * Create a new entry and enter it into the table.
  243.      */
  244.  
  245.     p = (char *) ckalloc((unsigned) (nameLength + strlen(value) + 2));
  246.     environ[index] = p;
  247.     strcpy(p, name);
  248.     p += nameLength;
  249.     *p = '=';
  250.     strcpy(p+1, value);
  251.  
  252.     /*
  253.      * Update all of the interpreters.
  254.      */
  255.  
  256.     for (eiPtr= firstInterpPtr; eiPtr != NULL; eiPtr = eiPtr->nextPtr) {
  257.     (void) Tcl_SetVar2(eiPtr->interp, "env", name, p+1, TCL_GLOBAL_ONLY);
  258.     }
  259. }
  260.  
  261. /*
  262.  *----------------------------------------------------------------------
  263.  *
  264.  * unsetenv --
  265.  *
  266.  *    Remove an environment variable, updating the "env" arrays
  267.  *    in all interpreters managed by us.
  268.  *
  269.  * Results:
  270.  *    None.
  271.  *
  272.  * Side effects:
  273.  *    Interpreters are updated, as is environ.
  274.  *
  275.  *----------------------------------------------------------------------
  276.  */
  277.  
  278. void
  279. unsetenv(name)
  280.     char *name;            /* Name of variable to remove. */
  281. {
  282.     int index, dummy;
  283.     char **envPtr;
  284.     EnvInterp *eiPtr;
  285.  
  286.     if (environSize == 0) {
  287.     EnvInit();
  288.     }
  289.  
  290.     /*
  291.      * Update the environ array.
  292.      */
  293.  
  294.     index = FindVariable(name, &dummy);
  295.     if (index == -1) {
  296.     return;
  297.     }
  298.     ckfree(environ[index]);
  299.     for (envPtr = environ+index+1; ; envPtr++) {
  300.     envPtr[-1] = *envPtr;
  301.     if (*envPtr == NULL) {
  302.         break;
  303.        }
  304.     }
  305.  
  306.     /*
  307.      * Update all of the interpreters.
  308.      */
  309.  
  310.     for (eiPtr = firstInterpPtr; eiPtr != NULL; eiPtr = eiPtr->nextPtr) {
  311.     (void) Tcl_UnsetVar2(eiPtr->interp, "env", name, TCL_GLOBAL_ONLY);
  312.     }
  313. }
  314.  
  315. /*
  316.  *----------------------------------------------------------------------
  317.  *
  318.  * EnvTraceProc --
  319.  *
  320.  *    This procedure is invoked whenever an environment variable
  321.  *    is modified or deleted.  It propagates the change to the
  322.  *    "environ" array and to any other interpreters for whom
  323.  *    we're managing an "env" array.
  324.  *
  325.  * Results:
  326.  *    Always returns NULL to indicate success.
  327.  *
  328.  * Side effects:
  329.  *    Environment variable changes get propagated.  If the whole
  330.  *    "env" array is deleted, then we stop managing things for
  331.  *    this interpreter (usually this happens because the whole
  332.  *    interpreter is being deleted).
  333.  *
  334.  *----------------------------------------------------------------------
  335.  */
  336.  
  337.     /* ARGSUSED */
  338. static char *
  339. EnvTraceProc(clientData, interp, name1, name2, flags)
  340.     ClientData clientData;    /* Not used. */
  341.     Tcl_Interp *interp;        /* Interpreter whose "env" variable is
  342.                  * being modified. */
  343.     char *name1;        /* Better be "env". */
  344.     char *name2;        /* Name of variable being modified, or
  345.                  * NULL if whole array is being deleted. */
  346.     int flags;            /* Indicates what's happening. */
  347. {
  348.     /*
  349.      * First see if the whole "env" variable is being deleted.  If
  350.      * so, just forget about this interpreter.
  351.      */
  352.  
  353.     if (name2 == NULL) {
  354.     register EnvInterp *eiPtr, *prevPtr;
  355.  
  356.     if ((flags & (TCL_TRACE_UNSETS|TCL_TRACE_DESTROYED))
  357.         != (TCL_TRACE_UNSETS|TCL_TRACE_DESTROYED)) {
  358.         panic("EnvTraceProc called with confusing arguments");
  359.     }
  360.     eiPtr = firstInterpPtr;
  361.     if (eiPtr->interp == interp) {
  362.         firstInterpPtr = eiPtr->nextPtr;
  363.     } else {
  364.         for (prevPtr = eiPtr, eiPtr = eiPtr->nextPtr; ;
  365.             prevPtr = eiPtr, eiPtr = eiPtr->nextPtr) {
  366.         if (eiPtr == NULL) {
  367.             panic("EnvTraceProc couldn't find interpreter");
  368.         }
  369.         if (eiPtr->interp == interp) {
  370.             prevPtr->nextPtr = eiPtr->nextPtr;
  371.             break;
  372.         }
  373.         }
  374.     }
  375.     ckfree((char *) eiPtr);
  376.     return NULL;
  377.     }
  378.  
  379.     /*
  380.      * If a value is being set, call setenv to do all of the work.
  381.      */
  382.  
  383.     if (flags & TCL_TRACE_WRITES) {
  384.     setenv(name2, Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY));
  385.     }
  386.  
  387.     if (flags & TCL_TRACE_UNSETS) {
  388.     unsetenv(name2);
  389.     }
  390.     return NULL;
  391. }
  392.  
  393. /*
  394.  *----------------------------------------------------------------------
  395.  *
  396.  * EnvInit --
  397.  *
  398.  *    This procedure is called to initialize our management
  399.  *    of the environ array.
  400.  *
  401.  * Results:
  402.  *    None.
  403.  *
  404.  * Side effects:
  405.  *    Environ gets copied to malloc-ed storage, so that in
  406.  *    the future we don't have to worry about which entries
  407.  *    are malloc-ed and which are static.
  408.  *
  409.  *----------------------------------------------------------------------
  410.  */
  411.  
  412. static void
  413. EnvInit()
  414. {
  415.     char **newEnviron;
  416.     int i, length;
  417.  
  418.     if (environSize != 0) {
  419.     return;
  420.     }
  421.     for (length = 0; environ[length] != NULL; length++) {
  422.     /* Empty loop body. */
  423.     }
  424.     environSize = length+5;
  425.     newEnviron = (char **) ckalloc((unsigned)
  426.         (environSize * sizeof(char *)));
  427.     for (i = 0; i < length; i++) {
  428.     newEnviron[i] = (char *) ckalloc((unsigned) (strlen(environ[i]) + 1));
  429.     strcpy(newEnviron[i], environ[i]);
  430.     }
  431.     newEnviron[length] = NULL;
  432.     environ = newEnviron;
  433. }
  434.